/* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2003 Validity Systems Inc.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution, if
*    any, must include the following acknowlegement:
*       "This product includes software developed by the
*        Validity Systems Inc."
*    Alternately, this acknowlegement may appear in the software itself,
*    if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "#Logger", "CS Logger", and "Validity Systems
*    Inc." must not be used to endorse or promote products derived
*    from this software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "#Logger"
*    nor may "#Logger" appear in their names without prior written
*    permission of Validity Systems Inc.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Validity Systems Inc.
*
*
*/

namespace VS
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading;

    /// <summary>
    /// Abstract class responsible managing (in the form of a queue) and dispatching log messages
    /// to their appropriate source.  The basic premises is that everything from a file writers, 
    /// to an e-mailer, to an syslog interface, to a db interface, etc... could be implemented and the only
    /// thing the implementer would need to worry about was the interface.
    /// </summary>
    public abstract class LoggerHandler
    {
        #region Fields
        /// <summary>
        /// Indicates whether this event handler is alive.
        /// </summary>
        private bool alive;

        /// <summary>
        /// Message queue.
        /// </summary>
        private Queue messageQueue;

        /// <summary>
        /// Event handler's thread.
        /// </summary>
        private Thread dispatch;

        private List<string> logLevelDescriptors = new List<string>();
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the LoggerHandler class. Default constructor.
        /// </summary>
        protected LoggerHandler()
        {
            this.messageQueue = Queue.Synchronized(new Queue(1000));
            this.Start();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the log level descriptions.
        /// </summary>
        protected List<string> LogLevelDescriptors
        {
            get { return this.logLevelDescriptors; }
        }
        #endregion

        #region Public methods
        /// <summary>
        /// Starts the execution of the Handler (e.g. Queue goes live)
        /// </summary>
        public void Start()
        {
            // It's already alive, nothing to do
            if (this.alive)
            {
                return;
            }

            // Make sure this is the only place where alive is set to true
            this.alive = true;
            this.dispatch = new Thread(new ThreadStart(this.DispatchMessages));
            this.dispatch.Start();
        }

        /// <summary>
        /// Stops the execution of the Handler gracefully (e.g. everything in the queue is
        /// dispatched, but nothing can be added to it)
        /// </summary>
        public void Shutdown()
        {
            // Nothing to do
            if (!this.alive)
            {
                return;
            }

            this.alive = false;
            Monitor.Enter(this.messageQueue);
            Monitor.PulseAll(this.messageQueue);
            Monitor.Exit(this.messageQueue);
        }

        /// <summary>
        /// Will immediately shutdown the thread without cleaning up or clearing
        /// out the queue.  Consequently this is not the recommended way to terminate.
        /// </summary>
        public void Abort()
        {
            if (!this.alive)
            {
                return;
            }

            this.alive = false;
            this.dispatch.Abort();
        }

        /// <summary>
        /// Responsible for queuing the log message.
        /// </summary>
        /// <param name="tag">Message's tag.</param>
        /// <param name="level">At which level to log.</param>
        /// <param name="levelDescription">Level's description.</param>
        /// <param name="message">Message to log.</param>
        public void Log(string tag, int level, string levelDescription, string message)
        {
            if (!this.alive)
            {
                return;
            }

            LoggerMessage lm = new LoggerMessage();
            lm.Message = message;
            lm.Tag = tag;
            lm.Level = level;
            lm.LevelDescription = levelDescription;
            lm.Time = System.DateTime.Now.ToFileTime();

            this.messageQueue.Enqueue(lm);

            Monitor.Enter(this.messageQueue);
            Monitor.PulseAll(this.messageQueue);
            Monitor.Exit(this.messageQueue);
        }
        #endregion

        #region Protected methods
        /// <summary>
        /// This is the worker method where the messages get dispatched
        /// </summary>
        protected void DispatchMessages()
        {
            // int max = 0;
            while (this.alive)
            {
                while ((this.messageQueue.Count != 0) && this.alive)
                {
                    this.Log((LoggerMessage)this.messageQueue.Dequeue());
                }

                if (this.alive && this.messageQueue.Count == 0)
                {
                    Monitor.Enter(this.messageQueue);
                    if (this.messageQueue.Count == 0)
                    {
                        Monitor.Wait(this.messageQueue);
                    }

                    Monitor.Exit(this.messageQueue);
                }
            }

            // You will only reach this code if you are exiting the program
            // This block ensures the messages that were queued up will get dumped
            while (this.messageQueue.Count != 0)
            {
                this.Log((LoggerMessage)this.messageQueue.Dequeue());
            }

            // Do any special shutdown you need to do
            this.OnShutdown();

            // Remove thread reference so you don't have object leak
            this.dispatch = null;
        }

        /// <summary>
        /// User implementable log method (e.g. what actions they need to take to perform
        /// a "log" operation, like logging to a file for example).
        /// </summary>
        /// <param name="message">Message to log.</param>
        protected abstract void Log(LoggerMessage message);

        /// <summary>
        /// User implementable, meant for cleanup when the thread is stopped (e.g. if you need to
        /// close files, db connections, etc...)
        /// </summary>
        protected abstract void OnShutdown();
        #endregion
    }
}
